/**
 * This object is in the gameobject package.
 */
package gameobjects;

/**
 * Author: Gunnar Sigurðsson
 * Date: 11/21/12
 * Time: 5:01 PM
 */

/**
 * The Board class generates the TicTacToe game board.
 */
public class Board {
    /**
     * A 2D array of Checkmark objects.
     */
    private Checkmark[][] board;
    /**
     * Player 1's marker.
     */
    private char markerP1;
    /**
     * Player 2's marker.
     */
    private char markerP2;
    /**
     * The winner of the game, null on initialization.
     */
    private Player winner;
    /**
     * Number of rows per game.
     */
    private final int nrRow = 3;
    /**
     * Number of columns per game.
     */
    private final int nrColumn = 3;

    /**
     * Initializes the Board class with a 3x3 array with each slot containing
     * a Checkmark object which contains all data for each slot.
     */
    public Board() {
        board = new Checkmark[nrRow][nrColumn];
        winner = null;
    }

    /**
     * Initializes the Board class with a 3x3 array with each slot containing
     * a Checkmark object which contains all data for each slot.
     * @param pMarkerP1 The char marker for Player 1
     * @param pMarkerP2 The char marker for Player 2
     * @see Checkmark
     */
    public Board(final char pMarkerP1, final char pMarkerP2) {
        this.markerP1 = pMarkerP1;
        this.markerP2 = pMarkerP2;
        board = new Checkmark[nrRow][nrColumn];
        winner = null;
    }

    /**
     * Only prints a pseudo board with numbered elements as a guide for the
     * user.
     */
    public final void generateNumberedBoard() {
        int number = 1;

        System.out.println("*** Each square has a number "
                + "ranging from 1-9...\n");
        for (int row = 0; row < nrRow; row++) {
            for (int column = 0; column < nrColumn; column++) {
                System.out.print("[" + number + "]");
                number++;
            }
            System.out.println();
        }
        System.out.println("\n*** When your turn is up, simply punch in "
                + "the number of the square you wish to check!\n");
    }

    /**
     * Fills the board array with Checkmark objects, giving each object a value
     * from 1-9 so the game knows which slot the user wants to occupy.
     * @see Checkmark
     */
    public final void generateEmptyBoard() {
        int value = 1;
        for (int row = 0; row < nrRow; row++) {
            for (int column = 0; column < nrColumn; column++) {
                board[row][column] = new Checkmark(value);
                value++;
            }
        }
    }

    /**
     * Prints the board as it currently is, showing which slots players have
     * occupied.
     */
    public final void generateBoard() {
        for (int row = 0; row < nrRow; row++) {
            for (int column = 0; column < nrColumn; column++) {
                System.out.print("[" + board[row][column].getMark() + "]");
            }
            System.out.println();
        }
    }

    /**
     * @param move The number of the slot the player wants to occupy
     * @param mark The players mark, usually an 'X' or an 'O'
     * @return true if the slot was not occupied before and the function
     * succeeded in marking it with the players mark.false if the slot was
     * occupied and marking it was unsuccessful
     */
    public final boolean setChecked(final int move, final char mark) {
        for (int row = 0; row < nrRow; row++) {
            for (int column = 0; column < nrColumn; column++) {
                // Check if the slot is occupied.
                if (board[row][column].getValue() == move
                        && !board[row][column].isChecked()) {
                    // If it is unoccupied then mark the slot with
                    // the player's mark.
                    board[row][column].setChecked(mark);
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Checks the board to see if the last move made has won either
     * player the game.
     * @return true if the game has been won, false if the game is still going
     */
    public final boolean checkForWinner() {

        char current = ' ';

        // Checking straight rows
        for (int row = 0; row < nrRow; row++) {
            for (int column = 0; column < nrColumn; column++) {
                if (!Character.isLetter(board[row][column].getMark())) {
                    break;
                }
                if (column == 0) {
                    current = board[row][column].getMark();
                } else if (current != board[row][column].getMark()) {
                    break;
                }
                if (column == 2) {
                    return true;
                }
            }
        }

        // Checking straight columns
        for (int column = 0; column < nrColumn; column++) {
            for (int row = 0; row < nrRow; row++) {
                if (!Character.isLetter(board[row][column].getMark())) {
                    break;
                }
                if (row == 0) {
                    current = board[row][column].getMark();
                } else if (current != board[row][column].getMark()) {
                    break;
                }
                if (row == 2) {
                    return true;
                }
            }
        }

        /** Checks diagonal lines
         * [X][ ][ ]
         * [ ][X][ ]
         * [ ][ ][X]
         */
        if ((board[0][0].getMark() == this.getMarkerP1()
                || board[0][0].getMark() == this.getMarkerP2())
                && board[0][0].getMark() == board[1][1].getMark()
                && board[1][1].getMark() == board[2][2].getMark()) {
            return true;
        }

        /** Checks diagonal lines
         * [ ][ ][X]
         * [ ][X][ ]
         * [X][ ][ ]
         */
        if ((board[2][0].getMark() == this.getMarkerP1()
                || board[2][0].getMark() == this.getMarkerP2())
                && board[2][0].getMark() == board[1][1].getMark()
                && board[1][1].getMark() == board[0][2].getMark()) {
            return true;
        }
        return false;
    }

    /**
     * Checks the board to see if a player has won. Sets the winner of the game
     * if it has been won.
     * @param player to check if this player won
     * @return true if a player has won the game. False if the game is still
     *going
     */
    public final boolean checkForWinner(final Player player) {
        if (checkForWinner()) {
           setWinner(player);
            return true;
        }
        return false;
    }

    /**
     * Returns BoardObject.
     * @return Board
     */
    public final Checkmark[][] getBoard() {
        return board;
    }

    /**
     * Returns the marker from player one.
     * @return Character
     */
    public final char getMarkerP1() {
        return markerP1;
    }

    /**
     * Returns the marker from player two.
     * @return Character
     */
    public final char getMarkerP2() {
        return markerP2;
    }

    /**
     * Gets a winner of the game.
     * @return winner
     */
    public final Player getWinner() {
        return winner;
    }

    /**
     * Sets a winner to the game.
     * @param pWinner to set the player object to the winner
     */
    public final void setWinner(final Player pWinner) {
        this.winner = pWinner;
    }
}
